index.js ➔ resample_   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The resample function.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
import { Interpolator } from './interpolator';
31
import { FIRLPF } from './fir-lpf';
32
import { ButterworthLPF } from './butterworth-lpf';
33
34
/**
35
 * Default use of LPF for each resampling method.
36
 * @readonly
37
 * @enum {boolean}
38
 * @private
39
 */
40
const DEFAULT_LPF_USE = {
41
  'point': false,
42
  'linear': false,
43
  'cubic': true,
44
  'sinc': true
45
};
46
47
/**
48
 * Default LPF order for each type of LPF.
49
 * @readonly
50
 * @enum {number}
51
 * @private
52
 */
53
const DEFAULT_LPF_ORDER = {
54
  'IIR': 16,
55
  'FIR': 71
56
};
57
58
/**
59
 * Default LPF class for each type of LPF.
60
 * @readonly
61
 * @enum {!Function}
62
 * @private
63
 */
64
const DEFAULT_LPF = {
65
  'IIR': ButterworthLPF,
66
  'FIR': FIRLPF
67
};
68
69
/**
70
 * Change the sample rate of the samples to a new sample rate.
71
 * @param {!Array<number>|!TypedArray} samples The original samples.
72
 * @param {number} oldSampleRate The original sample rate.
73
 * @param {number} sampleRate The target sample rate.
74
 * @param {Object=} options The extra configuration, if needed.
75
 * @return {!Float64Array} the new samples.
76
 */
77
export function resample(samples, oldSampleRate, sampleRate, options=null) {
78
  options = options || {};
79
  // Make the new sample container
80
  /** @type {number} */
81
  let rate = ((sampleRate - oldSampleRate) / oldSampleRate) + 1;
82
  /** @type {!Float64Array} */
83
  let newSamples = new Float64Array(samples.length * (rate));
84
  // Create the interpolator
85
  options.method = options.method || 'cubic';
86
  /** @type {!Object} */
87
  let interpolator = new Interpolator(
88
    samples.length,
89
    newSamples.length,
90
    {
91
      method: options.method,
92
      tension: options.tension || 0,
93
      sincFilterSize: options.sincFilterSize || 6,
94
      sincWindow: options.sincWindow || undefined,
95
      clip: options.clip || 'mirror'
96
    });
97
  // Resample + LPF
98
  if (options.LPF === undefined) {
99
    options.LPF = DEFAULT_LPF_USE[options.method];
100
  } 
101
  if (options.LPF) {
102
    options.LPFType = options.LPFType || 'IIR';
103
    const LPF = DEFAULT_LPF[options.LPFType];
104
    // Upsampling
105
    if (sampleRate > oldSampleRate) {
106
      /** @type {!Object} */
107
      let filter = new LPF(
108
        options.LPForder || DEFAULT_LPF_ORDER[options.LPFType],
109
        sampleRate,
110
        (oldSampleRate / 2));
111
      upsample_(
112
        samples, newSamples, interpolator, filter);
113
    // Downsampling
114
    } else {
115
      /** @type {!Object} */
116
      let filter = new LPF(
117
        options.LPForder || DEFAULT_LPF_ORDER[options.LPFType],
118
        oldSampleRate,
119
        sampleRate / 2);
120
      downsample_(
121
        samples, newSamples, interpolator, filter);
122
    }
123
  // Resample, no LPF
124
  } else {
125
    resample_(samples, newSamples, interpolator);
126
  }
127
  return newSamples;
128
}
129
130
/**
131
 * Resample.
132
 * @param {!Array<number>|!TypedArray} samples The original samples.
133
 * @param {!Float64Array} newSamples The container for the new samples.
134
 * @param {Object} interpolator The interpolator.
135
 * @private
136
 */
137
function resample_(samples, newSamples, interpolator) {
138
  // Resample
139
  for (let i = 0, len = newSamples.length; i < len; i++) {
140
    newSamples[i] = interpolator.interpolate(i, samples);
141
  }
142
}
143
144
/**
145
 * Upsample with LPF.
146
 * @param {!Array<number>|!TypedArray} samples The original samples.
147
 * @param {!Float64Array} newSamples The container for the new samples.
148
 * @param {Object} interpolator The interpolator.
149
 * @param {Object} filter The LPF object.
150
 * @private
151
 */
152
function upsample_(samples, newSamples, interpolator, filter) {
153
  // Resample and filter
154
  for (let i = 0, len = newSamples.length; i < len; i++) {
155
    newSamples[i] = filter.filter(interpolator.interpolate(i, samples));
156
  }
157
  // Reverse filter
158
  filter.reset();
159
  for (let i = newSamples.length - 1; i >= 0; i--) {
160
    newSamples[i]  = filter.filter(newSamples[i]);
161
  }
162
}
163
164
/**
165
 * Downsample with LPF.
166
 * @param {!Array<number>|!TypedArray} samples The original samples.
167
 * @param {!Float64Array} newSamples The container for the new samples.
168
 * @param {Object} interpolator The interpolator.
169
 * @param {Object} filter The LPF object.
170
 * @private
171
 */
172
function downsample_(samples, newSamples, interpolator, filter) {
173
  // Filter
174
  for (let i = 0, len = samples.length; i < len; i++) {
175
    samples[i]  = filter.filter(samples[i]);
176
  }
177
  // Reverse filter
178
  filter.reset();
179
  for (let i = samples.length - 1; i >= 0; i--) {
180
    samples[i]  = filter.filter(samples[i]);
181
  }
182
  // Resample
183
  resample_(samples, newSamples, interpolator);
184
}
185